package it.free.maya;

import java.util.Hashtable;
import java.util.StringTokenizer;

import org.apache.log4j.Logger;

/**
 * Keep the list of solr servers and their backups
 * @author Stefano Falconetti
 */
public class SolrShardsRegistrySingletonImpl extends Hashtable<String, SolrShard> {
	
	private static boolean configured = false;
	
	private static Logger logger = Logger.getLogger(SolrShardsRegistrySingletonImpl.class);
	private static SolrShardsRegistrySingletonImpl SOLR_HOSTS_REGISTRY = null;
	private static int size = 0;
	
	private static final String TOKEN_SEPARATOR = ";";
	private static final String TOKEN_PRIMARY_BACKUP_SEPARATOR = "@";
	private static final String TOKEN_PING_URL_SEPARATOR = "#";
	private static final String SOLR_HOSTS_PROPERTY_NAME = "solr.shards.hosts";
	
	private static final long serialVersionUID = 4706493346246041064L;
	
	/**
	 * Hidden constructor
	 */
	private SolrShardsRegistrySingletonImpl(){
		super();
	}

	/**
	 * Access point for the only object instance to be used 
	 * @return the singleton instance
	 */
	public static final SolrShardsRegistrySingletonImpl getInstance(){
		
		if(SolrShardsRegistrySingletonImpl.SOLR_HOSTS_REGISTRY == null){
		   synchronized(SolrShardsRegistrySingletonImpl.class){

			   if(SolrShardsRegistrySingletonImpl.SOLR_HOSTS_REGISTRY == null){
				  SolrShardsRegistrySingletonImpl.SOLR_HOSTS_REGISTRY = new SolrShardsRegistrySingletonImpl();
				  configure();
			   }
		   }
		}
		return SolrShardsRegistrySingletonImpl.SOLR_HOSTS_REGISTRY;
	}
	
	/**
	 * Do the activities needed to initialize
	 */
	private static final void configure(){
		
		String solrHosts = SolrShardsPropertiesLoader.getProperty(SOLR_HOSTS_PROPERTY_NAME);
		
		if( (solrHosts == null) || 
			(solrHosts.trim().equals("")) ){
			logger.error("Property [" + SOLR_HOSTS_PROPERTY_NAME + "] not configured, unable to use shard and backup configurations");
			SolrShardsRegistrySingletonImpl.configured = false;
			return;
		}
		
		try{
			parseShardsConfiguration(solrHosts);
			SolrShardsRegistrySingletonImpl.configured = true;
		}catch(Exception exc){
			   SolrShardsRegistrySingletonImpl.configured = false;
			   logger.info("Error configuring solrj extensions:", exc);
		}
	}
	
	/**
	 * Parse the configuration and load the hosts
	 * @param shards the string to parse
	 * @throws Exception
	 */
	private static final void parseShardsConfiguration(String shards) throws Exception{
		
		logger.info("Parsing configuration: [" + shards + "]");
		
		StringTokenizer perHostConfigurationTokenizer = new StringTokenizer(shards, SolrShardsRegistrySingletonImpl.TOKEN_SEPARATOR);
		StringTokenizer perHostDetailedConfigurationTokenizer = null;
		StringTokenizer perHostPingUrlConfigurationTokenizer = null;
		
		String perHostConfiguration = null;
		String primaryHostConfigurationToken = null;
		String backupHostConfigurationToken = null;
		String pingHostConfigurationToken = null;
		String perHostQueryUrlConfiguration = null;
		
		SolrShard solrShard = null;
		
		SolrCore primary = null;
		SolrCore backup = null;
		
		while(perHostConfigurationTokenizer.hasMoreElements()){
			  
			  perHostConfiguration = perHostConfigurationTokenizer.nextToken();
			  	  
			  perHostDetailedConfigurationTokenizer = new StringTokenizer(perHostConfiguration, SolrShardsRegistrySingletonImpl.TOKEN_PRIMARY_BACKUP_SEPARATOR);
		
			  primaryHostConfigurationToken = perHostDetailedConfigurationTokenizer.nextToken();
			  backupHostConfigurationToken = perHostDetailedConfigurationTokenizer.nextToken();
		
			  logger.info("Found shard: [" + primaryHostConfigurationToken + "], with backup on: [" + backupHostConfigurationToken + "]");
			  
			  perHostPingUrlConfigurationTokenizer = new StringTokenizer(primaryHostConfigurationToken, SolrShardsRegistrySingletonImpl.TOKEN_PING_URL_SEPARATOR);
			  
			  perHostQueryUrlConfiguration = perHostPingUrlConfigurationTokenizer.nextElement().toString();
			  
			  pingHostConfigurationToken = perHostPingUrlConfigurationTokenizer.nextToken();
			  
			  primary = new SolrCore(perHostQueryUrlConfiguration, 
					  			 SolrShardType.PRIMARY, 
					  			 pingHostConfigurationToken);
			  // -------------------------  
			  perHostPingUrlConfigurationTokenizer = new StringTokenizer(backupHostConfigurationToken, 
					  													 SolrShardsRegistrySingletonImpl.TOKEN_PING_URL_SEPARATOR);
			  
			  perHostQueryUrlConfiguration = perHostPingUrlConfigurationTokenizer.nextElement().toString();
			  
			  if(perHostQueryUrlConfiguration.equals(SolrShardType.NONE.toString())){
				  pingHostConfigurationToken = perHostPingUrlConfigurationTokenizer.nextToken();
				  
				  backup = new SolrCore(perHostQueryUrlConfiguration,
						  			SolrShardType.NONE, 
						  			pingHostConfigurationToken);
			  }else{
				    pingHostConfigurationToken = perHostPingUrlConfigurationTokenizer.nextToken();
				  
				    backup = new SolrCore(perHostQueryUrlConfiguration,
						  			      SolrShardType.BACKUP, 
						  			      pingHostConfigurationToken);
			  }
			  
			  solrShard = new SolrShard(primary, backup);
			  
			  SolrShardsRegistrySingletonImpl.getInstance().put(solrShard.toString(), solrShard);
			  
			  size++;	  
		}
	}

	/**
	 * @return the configured
	 */
	public boolean isConfigured() {
		return SolrShardsRegistrySingletonImpl.configured;
	}

	/**
	 * @return the size of the shards loaded from configuration
	 */
	public int getSize() {
		return SolrShardsRegistrySingletonImpl.size;
	}
	
	/**
	 * @return true if shards are present, false in case of single server configuration
	 */
	public boolean hasShards() {
		return (SolrShardsRegistrySingletonImpl.size == 1);
	}
	
}
